using System.ComponentModel;
using EnvDTE;
using Microsoft.Practices.RecipeFramework;
using Microsoft.Practices.RecipeFramework.VisualStudio.Library.Templates;
using VSSDKAssist.Assets.Actions.Base;
using VSSDKAssist.Assets.Actions.VisualStudio.VisualStudio;
using VSSDKAssist.Assets.Helpers;
using VSSDKAssist.Enums;
using VSSDKAssist.Recipes.Common.Actions;

namespace VSSDKAssist.Recipes.CreateEventSubscription
{
    /// <summary>
    /// Action that creates a VS Event subscription
    /// </summary>
    public class CreateEventSubscriptionAction : ActionBase
    {
        #region Properties
        private Project project;

        /// <summary>
        /// Gets or sets the project.
        /// </summary>
        /// <value>The project.</value>
        [Input(Required = true)]
        public Project Project
        {
            get { return project; }
            set { project = value; }
        }

        private CodeClass codeClass;

        /// <summary>
        /// Gets or sets the code class.
        /// </summary>
        /// <value>The code class.</value>
        [Input(Required = true)]
        public CodeClass CodeClass
        {
            get { return codeClass; }
            set { codeClass = value; }
        }

        private VSEvent vsEvent;

        /// <summary>
        /// Gets or sets the VS event.
        /// </summary>
        /// <value>The VS event.</value>
        [Input(Required = true)]
        public VSEvent VSEvent
        {
            get { return vsEvent; }
            set { vsEvent = value; }
        }

        private string commandId;

        /// <summary>
        /// Gets or sets the command id.
        /// </summary>
        /// <value>The command id.</value>
        [Input(Required = false)]
        public string CommandId
        {
            get { return commandId; }
            set { commandId = value; }
        }

        private string commandGuid;

        /// <summary>
        /// Gets or sets the command GUID.
        /// </summary>
        /// <value>The command GUID.</value>
        [Input(Required = false)]
        public string CommandGuid
        {
            get { return commandGuid; }
            set { commandGuid = value; }
        }
        #endregion

        /// <summary>
        /// Called when [execute].
        /// </summary>
        protected override void OnExecute()
        {
            bool addRefenceToDTE = false;

            string eventImplementationTemplate = 
                string.Format("{0}{1}",
                    VSSDKAssist.Recipes.Common.Helpers.CommonHelper.GetTTRelativePath(this.codeClass),
                    @"\EventSubscription\");

            string initializeCallTemplate = 
                string.Format("{0}{1}",
                    VSSDKAssist.Recipes.Common.Helpers.CommonHelper.GetTTRelativePath(this.codeClass),
                    @"\EventSubscription\");

            string disposeCallTemplate =
                string.Format("{0}{1}",
                    VSSDKAssist.Recipes.Common.Helpers.CommonHelper.GetTTRelativePath(this.codeClass),
                    @"\EventSubscription\");

            switch(this.vsEvent)
            {
                case VSEvent.BuildEvents:
                    eventImplementationTemplate += "BuildEvents.tt";
                    initializeCallTemplate += "BuildEventsInitializeCall.tt";
                    disposeCallTemplate += "BuildEventsDisposeCall.tt";
                    addRefenceToDTE = true;
                    break;
                case VSEvent.CommandEvents:
                    eventImplementationTemplate += "CommandEvents.tt";
                    initializeCallTemplate += "CommandEventsInitializeCall.tt";
                    disposeCallTemplate += "CommandEventsDisposeCall.tt";
                    addRefenceToDTE = true;
                    break;
                case VSEvent.ProjectItemEvents:
                    eventImplementationTemplate += "ProjectItemEvents.tt";
                    initializeCallTemplate += "ProjectItemEventsInitializeCall.tt";
                    disposeCallTemplate += "ProjectItemEventsDisposeCall.tt";
                    addRefenceToDTE = true;
                    break;
                case VSEvent.SolutionEvents:
                    eventImplementationTemplate += "SolutionEvents.tt";
                    initializeCallTemplate += "SolutionEventsInitializeCall.tt";
                    disposeCallTemplate += "SolutionEventsDisposeCall.tt";
                    this.codeClass.AddImplementedInterface(
                        "Microsoft.VisualStudio.Shell.Interop.IVsSolutionEvents3", -1);
                    break;
                case VSEvent.RunningDocTableEvents:
                    eventImplementationTemplate += "RDTEvents.tt";
                    initializeCallTemplate += "RDTEventsInitializeCall.tt";
                    disposeCallTemplate += "RDTEventsDisposeCall.tt";
                    this.codeClass.AddImplementedInterface(
                        "Microsoft.VisualStudio.Shell.Interop.IVsRunningDocTableEvents3", -1);
                    break;
                case VSEvent.FileChangeEvents:
                    eventImplementationTemplate += "FileChangeEvents.tt";
                    initializeCallTemplate += "FileChangeEventsInitializeCall.tt";
                    disposeCallTemplate += "FileChangeEventsDisposeCall.tt";
                    this.codeClass.AddImplementedInterface(
                        "Microsoft.VisualStudio.Shell.Interop.IVsFileChangeEvents", -1);
                    break;
                default:
                    return;
            }

            //Add/Update Initialize method
            using(AddVSPackageInitializeFunctionAction addVSPackageInitializeFunctionAction = new AddVSPackageInitializeFunctionAction())
            {
                addVSPackageInitializeFunctionAction.Site = this.Site;
                addVSPackageInitializeFunctionAction.CodeClass = this.CodeClass;
                addVSPackageInitializeFunctionAction.Execute();

                using(TextTemplateAction textTemplateAction1 = new TextTemplateAction())
                {
                    textTemplateAction1.Site = this.Site;
                    textTemplateAction1.Template = initializeCallTemplate;
                    textTemplateAction1.Execute();

                    FileCodeModelHelper.InsertTextToCodeElement(
                        addVSPackageInitializeFunctionAction.CodeFunction as CodeElement, textTemplateAction1.Content);
                }
            }

            //Add/Update Dispose method
            using(AddVSPackageDisposeFunctionAction addVSPackageDisposeFunctionAction = new AddVSPackageDisposeFunctionAction())
            {
                addVSPackageDisposeFunctionAction.Site = this.Site;
                addVSPackageDisposeFunctionAction.CodeClass = this.CodeClass;
                addVSPackageDisposeFunctionAction.Execute();

                using(TextTemplateAction textTemplateAction2 = new TextTemplateAction())
                {
                    textTemplateAction2.Site = this.Site;
                    textTemplateAction2.Template = disposeCallTemplate;
                    textTemplateAction2.Execute();

                    FileCodeModelHelper.InsertTextToCodeElement(
                        addVSPackageDisposeFunctionAction.CodeFunction as CodeElement, textTemplateAction2.Content);
                }
            }

            //Add subscription implementation
            using(TextTemplateAction textTemplateAction3 = new TextTemplateAction())
            {                
                textTemplateAction3.Template = eventImplementationTemplate;

                if(this.VSEvent == VSEvent.CommandEvents)
                {
                    TypeDescriptor.GetProperties(textTemplateAction3)["CommandId"].SetValue(textTemplateAction3, this.CommandId);
                    TypeDescriptor.GetProperties(textTemplateAction3)["CommandGuid"].SetValue(textTemplateAction3, this.CommandGuid);
                }

                textTemplateAction3.Site = this.Site;

                textTemplateAction3.Execute();

                FileCodeModelHelper.InsertTextToCodeElement(
                    this.CodeClass as CodeElement, textTemplateAction3.Content);
            }

            if(addRefenceToDTE)
            {
                //Add assembly references
                using(AddProjectReferenceAction addProjectReferenceAction = new AddProjectReferenceAction())
                {
                    addProjectReferenceAction.Site = this.Site;
                    addProjectReferenceAction.ReferencedAssembly = "EnvDTE.dll";
                    addProjectReferenceAction.ReferringProject = this.Project;

                    addProjectReferenceAction.Execute();
                }
            }
        }
    }
}